{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "87773ce7",
   "metadata": {
    "id": "87773ce7"
   },
   "source": [
    "# Search using query rules\n",
    "\n",
    "<a target=\"_blank\" href=\"https://colab.research.google.com/github/elastic/elasticsearch-labs/blob/main/notebooks/search/05-query-rules.ipynb\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>\n",
    "\n",
    "This interactive notebook will introduce you to how use query rules, using the official [Elasticsearch Python client](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html).\n",
    "You'll store query rules in Elasticsearch using the [query rules API](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-rules-apis.html) and query them using [rule_query](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-rule-query.html)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a32202e2",
   "metadata": {
    "id": "a32202e2"
   },
   "source": [
    "## Create Elastic Cloud deployment\n",
    "\n",
    "If you don't have an Elastic Cloud deployment, sign up [here](https://cloud.elastic.co/registration?onboarding_token=search&utm_source=github&utm_content=elasticsearch-labs-notebook) for a free trial.\n",
    "\n",
    "Once logged in to your Elastic Cloud account, go to the [Create deployment](https://cloud.elastic.co/deployments/create) page and select **Create deployment**. Make sure the Elasticsearch version is **8.10.0** or newer. Leave all other settings with their default values."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "52a6a607",
   "metadata": {
    "id": "52a6a607"
   },
   "source": [
    "## Install packages and import modules\n",
    "\n",
    "To get started, we'll need to connect to our Elastic deployment using the Python client.\n",
    "Because we're using an Elastic Cloud deployment, we'll use the **Cloud ID** to identify our deployment.\n",
    "\n",
    "First we need to install the `elasticsearch` Python client."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ffc5fa6f",
   "metadata": {
    "id": "ffc5fa6f"
   },
   "outputs": [],
   "source": [
    "!pip install -qU \"elasticsearch<9\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0241694c",
   "metadata": {
    "id": "0241694c"
   },
   "source": [
    "## Initialize the Elasticsearch client\n",
    "\n",
    "Now we can instantiate the [Elasticsearch python client](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/index.html), providing the cloud id and password in your deployment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f38e0397",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "f38e0397",
    "outputId": "ad6df489-d242-4229-a42a-39c5ca19d124"
   },
   "outputs": [],
   "source": [
    "from elasticsearch import Elasticsearch\n",
    "from getpass import getpass\n",
    "\n",
    "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id\n",
    "ELASTIC_CLOUD_ID = getpass(\"Elastic Cloud ID: \")\n",
    "\n",
    "# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#creating-an-api-key\n",
    "ELASTIC_API_KEY = getpass(\"Elastic Api Key: \")\n",
    "\n",
    "# Create the client instance\n",
    "client = Elasticsearch(\n",
    "    # For local development\n",
    "    # hosts=[\"http://localhost:9200\"]\n",
    "    cloud_id=ELASTIC_CLOUD_ID,\n",
    "    api_key=ELASTIC_API_KEY,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fcd165fa",
   "metadata": {
    "id": "fcd165fa"
   },
   "source": [
    "If you're running Elasticsearch locally or self-managed, you can pass in the Elasticsearch host instead. [Read more](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#_verifying_https_with_certificate_fingerprints_python_3_10_or_later) on how to connect to Elasticsearch locally."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bbc6f297-9773-4240-950e-fa7b4856bb81",
   "metadata": {},
   "source": [
    "### Enable Telemetry\n",
    "\n",
    "Knowing that you are using this notebook helps us decide where to invest our efforts to improve our products. We would like to ask you that you run the following code to let us gather anonymous usage statistics. See [telemetry.py](https://github.com/elastic/elasticsearch-labs/blob/main/telemetry/telemetry.py) for details. Thank you!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "92d0cadb-4c4b-4ca5-a53f-a36f8f3e9650",
   "metadata": {},
   "outputs": [],
   "source": [
    "!curl -O -s https://raw.githubusercontent.com/elastic/elasticsearch-labs/main/telemetry/telemetry.py\n",
    "from telemetry import enable_telemetry\n",
    "\n",
    "client = enable_telemetry(client, \"05-query-rules\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1462ebd8",
   "metadata": {
    "id": "1462ebd8"
   },
   "source": [
    "### Test the Client\n",
    "Before you continue, confirm that the client has connected with this test."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "25c618eb",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "25c618eb",
    "outputId": "30a6ba5b-5109-4457-ddfe-5633a077ca9b"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'name': 'instance-0000000011', 'cluster_name': 'd1bd36862ce54c7b903e2aacd4cd7f0a', 'cluster_uuid': 'tIkh0X_UQKmMFQKSfUw-VQ', 'version': {'number': '8.11.1', 'build_flavor': 'default', 'build_type': 'docker', 'build_hash': '6f9ff581fbcde658e6f69d6ce03050f060d1fd0c', 'build_date': '2023-11-11T10:05:59.421038163Z', 'build_snapshot': False, 'lucene_version': '9.8.0', 'minimum_wire_compatibility_version': '7.17.0', 'minimum_index_compatibility_version': '7.0.0'}, 'tagline': 'You Know, for Search'}\n"
     ]
    }
   ],
   "source": [
    "print(client.info())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61e1e6d8",
   "metadata": {
    "id": "61e1e6d8"
   },
   "source": [
    "## Index some test data\n",
    "\n",
    "Our client is set up and connected to our Elastic deployment.\n",
    "Now we need some data to test out the basics of Elasticsearch queries.\n",
    "We'll use a small index of products with the following fields:\n",
    "\n",
    "- `name`\n",
    "- `description`\n",
    "- `price`\n",
    "- `currency`\n",
    "- `plug_type`\n",
    "- `voltage`"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "075f5eb6",
   "metadata": {
    "id": "075f5eb6"
   },
   "source": [
    "### Index test data\n",
    "\n",
    "Run the following command to upload some sample data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "008d723e",
   "metadata": {
    "id": "008d723e"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ObjectApiResponse({'errors': False, 'took': 10, 'items': [{'index': {'_index': 'products_index', '_id': 'us1', '_version': 2, 'result': 'updated', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 4, '_primary_term': 3, 'status': 200}}, {'index': {'_index': 'products_index', '_id': 'uk1', '_version': 2, 'result': 'updated', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 5, '_primary_term': 3, 'status': 200}}, {'index': {'_index': 'products_index', '_id': 'eu1', '_version': 2, 'result': 'updated', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 6, '_primary_term': 3, 'status': 200}}, {'index': {'_index': 'products_index', '_id': 'preview1', '_version': 2, 'result': 'updated', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 7, '_primary_term': 3, 'status': 200}}]})"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import json\n",
    "from urllib.request import urlopen\n",
    "\n",
    "url = \"https://raw.githubusercontent.com/elastic/elasticsearch-labs/main/notebooks/search/query-rules-data.json\"\n",
    "response = urlopen(url)\n",
    "docs = json.loads(response.read())\n",
    "\n",
    "operations = []\n",
    "for doc in docs:\n",
    "    operations.append({\"index\": {\"_index\": \"products_index\", \"_id\": doc[\"id\"]}})\n",
    "    operations.append(doc[\"content\"])\n",
    "client.bulk(index=\"products_index\", operations=operations, refresh=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "985c9992",
   "metadata": {},
   "source": [
    "## Search the test data\n",
    "\n",
    "First, let's search our data for a reliable wireless charger.\n",
    "\n",
    "Before we search our data, we'll define some convenience functions to output the raw JSON responses from Elasticsearch into a more human-readable format."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "54b40035",
   "metadata": {
    "source_hidden": true
   },
   "outputs": [],
   "source": [
    "def pretty_response(response):\n",
    "    if len(response[\"hits\"][\"hits\"]) == 0:\n",
    "        print(\"Your search returned no results.\")\n",
    "    else:\n",
    "        for hit in response[\"hits\"][\"hits\"]:\n",
    "            id = hit[\"_id\"]\n",
    "            score = hit[\"_score\"]\n",
    "            name = hit[\"_source\"][\"name\"]\n",
    "            description = hit[\"_source\"][\"description\"]\n",
    "            price = hit[\"_source\"][\"price\"]\n",
    "            currency = hit[\"_source\"][\"currency\"]\n",
    "            plug_type = hit[\"_source\"][\"plug_type\"]\n",
    "            voltage = hit[\"_source\"][\"voltage\"]\n",
    "            pretty_output = f\"\\nID: {id}\\nName: {name}\\nDescription: {description}\\nPrice: {price}\\nCurrency: {currency}\\nPlug type: {plug_type}\\nVoltage: {voltage}\\nScore: {score}\"\n",
    "            print(pretty_output)\n",
    "\n",
    "\n",
    "def pretty_ruleset(response):\n",
    "    print(\"Ruleset ID: \" + response[\"ruleset_id\"])\n",
    "    for rule in response[\"rules\"]:\n",
    "        rule_id = rule[\"rule_id\"]\n",
    "        type = rule[\"type\"]\n",
    "        print(f\"\\nRule ID: {rule_id}\\n\\tType: {type}\\n\\tCriteria:\")\n",
    "        criteria = rule[\"criteria\"]\n",
    "        for rule_criteria in criteria:\n",
    "            criteria_type = rule_criteria[\"type\"]\n",
    "            metadata = rule_criteria[\"metadata\"]\n",
    "            values = rule_criteria[\"values\"]\n",
    "            print(f\"\\t\\t{metadata} {criteria_type} {values}\")\n",
    "        ids = rule[\"actions\"][\"ids\"]\n",
    "        print(f\"\\tPinned ids: {ids}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cdfa3ad0",
   "metadata": {},
   "source": [
    "Next, do the search:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ed5c572a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "ID: eu1\n",
      "Name: PureJuice Pro - Wireless Charger suitable for European plugs\n",
      "Description: PureJuice Pro: Elevating wireless charging. Combining unparalleled charging speeds with elegant design, it promises both rapid and dependable energy for your devices. Embrace the future of wireless charging.\n",
      "Price: 18.0\n",
      "Currency: EUR\n",
      "Plug type: C\n",
      "Voltage: 230V\n",
      "Score: 14.5004\n",
      "\n",
      "ID: preview1\n",
      "Name: PureJuice Pro - Pre-order next version\n",
      "Description: Newest version of the PureJuice Pro wireless charger, coming soon! The newest model of the PureJuice Pro boasts a 2x faster charge than the current model, and a sturdier cable with an eighteen month full warranty. We also have a battery backup to charge on-the-go, up to two full charges. Pre-order yours today!\n",
      "Price: 36.0\n",
      "Currency: USD\n",
      "Plug type: ['B', 'C', 'G']\n",
      "Voltage: ['230V', '120V']\n",
      "Score: 1.0668641\n",
      "\n",
      "ID: us1\n",
      "Name: PureJuice Pro\n",
      "Description: PureJuice Pro: Experience the pinnacle of wireless charging. Blending rapid charging tech with sleek design, it ensures your devices are powered swiftly and safely. The future of charging is here.\n",
      "Price: 15.0\n",
      "Currency: USD\n",
      "Plug type: B\n",
      "Voltage: 120v\n",
      "Score: 0.11306982\n",
      "\n",
      "ID: uk1\n",
      "Name: PureJuice Pro - UK Compatible\n",
      "Description: PureJuice Pro: Redefining wireless charging. Seamlessly merging swift charging capabilities with a refined aesthetic, it guarantees your devices receive rapid and secure power. Welcome to the next generation of charging.\n",
      "Price: 20.0\n",
      "Currency: GBP\n",
      "Plug type: G\n",
      "Voltage: 230V\n",
      "Score: 0.11306982\n"
     ]
    }
   ],
   "source": [
    "response = client.search(\n",
    "    index=\"products_index\",\n",
    "    query={\n",
    "        \"multi_match\": {\n",
    "            \"query\": \"reliable wireless charger for iPhone\",\n",
    "            \"fields\": [\"name^5\", \"description\"],\n",
    "        }\n",
    "    },\n",
    ")\n",
    "\n",
    "pretty_response(response)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa1b4af1",
   "metadata": {},
   "source": [
    "As we can see from the response, the European result is ranked first. This might not be desirable if, for example, I know that my searcher is coming from the US or the UK which have different plugs and specifications. \n",
    "\n",
    "Query rules can help here!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "39bdefe0",
   "metadata": {
    "id": "39bdefe0"
   },
   "source": [
    "## Creating rules\n",
    "\n",
    "Let's assume that separately, we know what country our users are coming from (perhaps geolocation from IP addresses or logged in user account information). Now, we want to create query rules to boost wireless chargers based on that information when people search for anything containing the phrase `wireless charger`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "b109d45d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ObjectApiResponse({'result': 'created'})"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "client.query_rules.put_ruleset(\n",
    "    ruleset_id=\"promotion-rules\",\n",
    "    rules=[\n",
    "        {\n",
    "            \"rule_id\": \"us-charger\",\n",
    "            \"type\": \"pinned\",\n",
    "            \"criteria\": [\n",
    "                {\n",
    "                    \"type\": \"contains\",\n",
    "                    \"metadata\": \"my_query\",\n",
    "                    \"values\": [\"wireless charger\"],\n",
    "                },\n",
    "                {\"type\": \"exact\", \"metadata\": \"country\", \"values\": [\"us\"]},\n",
    "            ],\n",
    "            \"actions\": {\"ids\": [\"us1\"]},\n",
    "        },\n",
    "        {\n",
    "            \"rule_id\": \"uk-charger\",\n",
    "            \"type\": \"pinned\",\n",
    "            \"criteria\": [\n",
    "                {\n",
    "                    \"type\": \"contains\",\n",
    "                    \"metadata\": \"my_query\",\n",
    "                    \"values\": [\"wireless charger\"],\n",
    "                },\n",
    "                {\"type\": \"exact\", \"metadata\": \"country\", \"values\": [\"uk\"]},\n",
    "            ],\n",
    "            \"actions\": {\"ids\": [\"uk1\"]},\n",
    "        },\n",
    "    ],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28ae167a",
   "metadata": {},
   "source": [
    "In order for these rules to match, one of the following must be true:\n",
    "\n",
    "- `my_query` contains the string \"wireless charger\" *AND* `country` is \"us\"\n",
    "- `my_query` contains the string \"wireless charger\" *AND* `country` is \"uk\"\n",
    "\n",
    "We can view our ruleset using the API as well (with another `pretty_ruleset` function for readability):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "dcda4989",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ruleset ID: promotion-rules\n",
      "\n",
      "Rule ID: us-charger\n",
      "\tType: pinned\n",
      "\tCriteria:\n",
      "\t\tmy_query contains ['wireless charger']\n",
      "\t\tcountry exact ['us']\n",
      "\tPinned ids: ['us1']\n",
      "\n",
      "Rule ID: uk-charger\n",
      "\tType: pinned\n",
      "\tCriteria:\n",
      "\t\tmy_query contains ['wireless charger']\n",
      "\t\tcountry exact ['uk']\n",
      "\tPinned ids: ['uk1']\n"
     ]
    }
   ],
   "source": [
    "response = client.query_rules.get_ruleset(ruleset_id=\"promotion-rules\")\n",
    "pretty_ruleset(response)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6369c6fc",
   "metadata": {},
   "source": [
    "Next, we use the rule_query to perform a search using the same organic query as above, but with the addition of query rules:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "Df7hwcIjYwMT",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "Df7hwcIjYwMT",
    "outputId": "e63884d7-d4a5-4f5d-ea43-fc2f0793f040"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "ID: us1\n",
      "Name: PureJuice Pro\n",
      "Description: PureJuice Pro: Experience the pinnacle of wireless charging. Blending rapid charging tech with sleek design, it ensures your devices are powered swiftly and safely. The future of charging is here.\n",
      "Price: 15.0\n",
      "Currency: USD\n",
      "Plug type: B\n",
      "Voltage: 120v\n",
      "Score: 1.7014122e+38\n",
      "\n",
      "ID: eu1\n",
      "Name: PureJuice Pro - Wireless Charger suitable for European plugs\n",
      "Description: PureJuice Pro: Elevating wireless charging. Combining unparalleled charging speeds with elegant design, it promises both rapid and dependable energy for your devices. Embrace the future of wireless charging.\n",
      "Price: 18.0\n",
      "Currency: EUR\n",
      "Plug type: C\n",
      "Voltage: 230V\n",
      "Score: 14.5004\n",
      "\n",
      "ID: preview1\n",
      "Name: PureJuice Pro - Pre-order next version\n",
      "Description: Newest version of the PureJuice Pro wireless charger, coming soon! The newest model of the PureJuice Pro boasts a 2x faster charge than the current model, and a sturdier cable with an eighteen month full warranty. We also have a battery backup to charge on-the-go, up to two full charges. Pre-order yours today!\n",
      "Price: 36.0\n",
      "Currency: USD\n",
      "Plug type: ['B', 'C', 'G']\n",
      "Voltage: ['230V', '120V']\n",
      "Score: 1.0668641\n",
      "\n",
      "ID: uk1\n",
      "Name: PureJuice Pro - UK Compatible\n",
      "Description: PureJuice Pro: Redefining wireless charging. Seamlessly merging swift charging capabilities with a refined aesthetic, it guarantees your devices receive rapid and secure power. Welcome to the next generation of charging.\n",
      "Price: 20.0\n",
      "Currency: GBP\n",
      "Plug type: G\n",
      "Voltage: 230V\n",
      "Score: 0.11306982\n"
     ]
    }
   ],
   "source": [
    "response = client.search(\n",
    "    index=\"products_index\",\n",
    "    query={\n",
    "        \"rule_query\": {\n",
    "            \"organic\": {\n",
    "                \"multi_match\": {\n",
    "                    \"query\": \"reliable wireless charger for iPhone\",\n",
    "                    \"fields\": [\"name^5\", \"description\"],\n",
    "                }\n",
    "            },\n",
    "            \"match_criteria\": {\n",
    "                \"my_query\": \"reliable wireless charger for iPhone\",\n",
    "                \"country\": \"us\",\n",
    "            },\n",
    "            \"ruleset_id\": \"promotion-rules\",\n",
    "        }\n",
    "    },\n",
    ")\n",
    "\n",
    "pretty_response(response)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac053cf3",
   "metadata": {},
   "source": [
    "The rule query boosts the documents that we want to be displayed first.\n",
    "\n",
    "Note that all criteria in a rule must match in order for a rule to be applied. If we update the `country` to be \"ca\" for example, neither query rule will be applied and we will return the organic, unmodified result set."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "f177d9fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "ID: eu1\n",
      "Name: PureJuice Pro - Wireless Charger suitable for European plugs\n",
      "Description: PureJuice Pro: Elevating wireless charging. Combining unparalleled charging speeds with elegant design, it promises both rapid and dependable energy for your devices. Embrace the future of wireless charging.\n",
      "Price: 18.0\n",
      "Currency: EUR\n",
      "Plug type: C\n",
      "Voltage: 230V\n",
      "Score: 14.5004\n",
      "\n",
      "ID: preview1\n",
      "Name: PureJuice Pro - Pre-order next version\n",
      "Description: Newest version of the PureJuice Pro wireless charger, coming soon! The newest model of the PureJuice Pro boasts a 2x faster charge than the current model, and a sturdier cable with an eighteen month full warranty. We also have a battery backup to charge on-the-go, up to two full charges. Pre-order yours today!\n",
      "Price: 36.0\n",
      "Currency: USD\n",
      "Plug type: ['B', 'C', 'G']\n",
      "Voltage: ['230V', '120V']\n",
      "Score: 1.0668641\n",
      "\n",
      "ID: us1\n",
      "Name: PureJuice Pro\n",
      "Description: PureJuice Pro: Experience the pinnacle of wireless charging. Blending rapid charging tech with sleek design, it ensures your devices are powered swiftly and safely. The future of charging is here.\n",
      "Price: 15.0\n",
      "Currency: USD\n",
      "Plug type: B\n",
      "Voltage: 120v\n",
      "Score: 0.11306982\n",
      "\n",
      "ID: uk1\n",
      "Name: PureJuice Pro - UK Compatible\n",
      "Description: PureJuice Pro: Redefining wireless charging. Seamlessly merging swift charging capabilities with a refined aesthetic, it guarantees your devices receive rapid and secure power. Welcome to the next generation of charging.\n",
      "Price: 20.0\n",
      "Currency: GBP\n",
      "Plug type: G\n",
      "Voltage: 230V\n",
      "Score: 0.11306982\n"
     ]
    }
   ],
   "source": [
    "response = client.search(\n",
    "    index=\"products_index\",\n",
    "    query={\n",
    "        \"rule_query\": {\n",
    "            \"organic\": {\n",
    "                \"multi_match\": {\n",
    "                    \"query\": \"reliable wireless charger for iPhone\",\n",
    "                    \"fields\": [\"name^5\", \"description\"],\n",
    "                }\n",
    "            },\n",
    "            \"match_criteria\": {\n",
    "                \"my_query\": \"reliable wireless charger for iPhone\",\n",
    "                \"country\": \"ca\",\n",
    "            },\n",
    "            \"ruleset_id\": \"promotion-rules\",\n",
    "        }\n",
    "    },\n",
    ")\n",
    "\n",
    "pretty_response(response)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f6e18f42",
   "metadata": {},
   "source": [
    "It's also possible for multiple rules to apply to a single rule query. Let's update our ruleset, to always pin a promotional result for a pre-order page for our newest model. \n",
    "\n",
    "Because rules are applied in order, we'll put the pre-order document at the beginning of the ruleset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "b78b5bda",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ObjectApiResponse({'result': 'updated'})"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "client.query_rules.put_ruleset(\n",
    "    ruleset_id=\"promotion-rules\",\n",
    "    rules=[\n",
    "        {\n",
    "            \"rule_id\": \"preorder\",\n",
    "            \"type\": \"pinned\",\n",
    "            \"criteria\": [{\"type\": \"always\"}],\n",
    "            \"actions\": {\"ids\": [\"preview1\"]},\n",
    "        },\n",
    "        {\n",
    "            \"rule_id\": \"us-charger\",\n",
    "            \"type\": \"pinned\",\n",
    "            \"criteria\": [\n",
    "                {\n",
    "                    \"type\": \"contains\",\n",
    "                    \"metadata\": \"my_query\",\n",
    "                    \"values\": [\"wireless charger\"],\n",
    "                },\n",
    "                {\"type\": \"exact\", \"metadata\": \"country\", \"values\": [\"us\"]},\n",
    "            ],\n",
    "            \"actions\": {\"ids\": [\"us1\"]},\n",
    "        },\n",
    "        {\n",
    "            \"rule_id\": \"uk-charger\",\n",
    "            \"type\": \"pinned\",\n",
    "            \"criteria\": [\n",
    "                {\n",
    "                    \"type\": \"contains\",\n",
    "                    \"metadata\": \"my_query\",\n",
    "                    \"values\": [\"wireless charger\"],\n",
    "                },\n",
    "                {\"type\": \"exact\", \"metadata\": \"country\", \"values\": [\"uk\"]},\n",
    "            ],\n",
    "            \"actions\": {\"ids\": [\"uk1\"]},\n",
    "        },\n",
    "    ],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "265501e6",
   "metadata": {},
   "source": [
    "Now, doing a search from the UK will pin the pre-order page first, then pin the UK result second, before returning the organic search results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "b66993ed",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "ID: preview1\n",
      "Name: PureJuice Pro - Pre-order next version\n",
      "Description: Newest version of the PureJuice Pro wireless charger, coming soon! The newest model of the PureJuice Pro boasts a 2x faster charge than the current model, and a sturdier cable with an eighteen month full warranty. We also have a battery backup to charge on-the-go, up to two full charges. Pre-order yours today!\n",
      "Price: 36.0\n",
      "Currency: USD\n",
      "Plug type: ['B', 'C', 'G']\n",
      "Voltage: ['230V', '120V']\n",
      "Score: 1.7014124e+38\n",
      "\n",
      "ID: uk1\n",
      "Name: PureJuice Pro - UK Compatible\n",
      "Description: PureJuice Pro: Redefining wireless charging. Seamlessly merging swift charging capabilities with a refined aesthetic, it guarantees your devices receive rapid and secure power. Welcome to the next generation of charging.\n",
      "Price: 20.0\n",
      "Currency: GBP\n",
      "Plug type: G\n",
      "Voltage: 230V\n",
      "Score: 1.7014122e+38\n",
      "\n",
      "ID: eu1\n",
      "Name: PureJuice Pro - Wireless Charger suitable for European plugs\n",
      "Description: PureJuice Pro: Elevating wireless charging. Combining unparalleled charging speeds with elegant design, it promises both rapid and dependable energy for your devices. Embrace the future of wireless charging.\n",
      "Price: 18.0\n",
      "Currency: EUR\n",
      "Plug type: C\n",
      "Voltage: 230V\n",
      "Score: 14.5004\n",
      "\n",
      "ID: us1\n",
      "Name: PureJuice Pro\n",
      "Description: PureJuice Pro: Experience the pinnacle of wireless charging. Blending rapid charging tech with sleek design, it ensures your devices are powered swiftly and safely. The future of charging is here.\n",
      "Price: 15.0\n",
      "Currency: USD\n",
      "Plug type: B\n",
      "Voltage: 120v\n",
      "Score: 0.11306982\n"
     ]
    }
   ],
   "source": [
    "response = client.search(\n",
    "    index=\"products_index\",\n",
    "    query={\n",
    "        \"rule_query\": {\n",
    "            \"organic\": {\n",
    "                \"multi_match\": {\n",
    "                    \"query\": \"reliable wireless charger for iPhone\",\n",
    "                    \"fields\": [\"name^5\", \"description\"],\n",
    "                }\n",
    "            },\n",
    "            \"match_criteria\": {\n",
    "                \"my_query\": \"reliable wireless charger for iPhone\",\n",
    "                \"country\": \"uk\",\n",
    "            },\n",
    "            \"ruleset_id\": \"promotion-rules\",\n",
    "        }\n",
    "    },\n",
    ")\n",
    "\n",
    "pretty_response(response)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e216df3",
   "metadata": {},
   "source": [
    "These examples start to show the power of promoting documents based on contextual query metadata. For more information on how to get started using query rules, check out our [blog post](https://www.elastic.co/blog/introducing-query-rules-elasticsearch-8-10) and [search guide](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-using-query-rules.html)."
   ]
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
